package com.vois.jack.btmgr.util;


public class DataRingBuffer {
    private Logger logger = Logger.getLogger(DataRingBuffer.class);
    private int tail, head;
    private int bufferSize;
    private byte[] buffer;
    private short[] shortBuffer;
    private String userName;
    private boolean isDestroying;

    public DataRingBuffer(String userName, int bufferSize, boolean usingByte) {
        DataRingBufferInternal(userName, bufferSize, usingByte);
    }

    public DataRingBuffer(int bufferSize, boolean usingByte) {
        DataRingBufferInternal("Default", bufferSize, usingByte);
    }

    private void DataRingBufferInternal(String userName, int bufferSize, boolean usingByte) {
        this.bufferSize = bufferSize;
        this.userName = userName;
        if (usingByte) {
            buffer = new byte[bufferSize];
        }else {
            shortBuffer = new short[bufferSize];
        }
        tail = head = 0;
        isDestroying = false;
    }

    public void reInitRingBuffer() {
        isDestroying = false;
        head = tail = 0;
    }

    private boolean isEmptyInternal() {
        return tail == head;
    }

    private int getFreeSizeInternal() {
        return (head - tail + bufferSize - 1) % bufferSize;
    }

    private int getDataSizeInternal() {
        return (tail - head + bufferSize) % (bufferSize);
    }

    public synchronized boolean isEmpty() {
        return isEmptyInternal();
    }

    public synchronized boolean isFull() {
        return (tail + 1) == head;
    }

    public synchronized int getFreeSize() {
        return getFreeSizeInternal();
    }

    public synchronized int getDataSize() {
        return getDataSizeInternal();
    }

    public synchronized int putData(short[] data, int dataOffset, int dataSize) throws InterruptedException {
        if (shortBuffer == null) {
            return 0;
        }
        while (getFreeSizeInternal() < dataSize) {
            //logger.d(userName + "not enough memory, waiting....");
            this.wait();
            if (isDestroying) {
                logger.d(userName + ":clear is executed, exit putData");
                return 0;
            }
        }

        for (int i = 0; i < dataSize; i++, tail++) {
            if (tail >= bufferSize) {
                tail %= bufferSize;
            }
            shortBuffer[tail] = data[dataOffset + i];
        }

        this.notifyAll();
        return dataSize;
    }

    public synchronized int putData(byte[] data, int dataOffset, int dataSize) throws InterruptedException {
        if (buffer == null) {
            return 0;
        }
        while (getFreeSizeInternal() < dataSize) {
            //logger.d(userName + "not enough memory, waiting....");
            this.wait();
            if (isDestroying) {
                logger.d(userName + ":clear is executed, exit putData");
                return 0;
            }
        }

        for (int i = 0; i < dataSize; i++, tail++) {
            if (tail >= bufferSize) {
                tail %= bufferSize;
            }
            buffer[tail] = data[dataOffset + i];
        }

        this.notifyAll();
        return dataSize;
    }

    public synchronized int getData(short[] data, int dataOffset, int dataSize) throws InterruptedException {
        if (shortBuffer == null) {
            return 0;
        }

        int needGetSize = dataSize;
        while (isEmptyInternal()) {
            //logger.d(userName + "is empty now, wait......");
            this.wait();
            if (isDestroying) {
                //logger.d(userName + ":clear is executed, exit getData");
                return 0;
            }
        }

        if (getDataSizeInternal() < dataSize) {
            needGetSize = getDataSizeInternal();
        }

        for (int i = 0; i < needGetSize; i++, head++) {
            if (head >= bufferSize) {
                head %= bufferSize;
            }
            data[i + dataOffset] = shortBuffer[head];
        }

        this.notifyAll();

        return needGetSize;
    }

    public synchronized int getData(byte[] data, int dataOffset, int dataSize) throws InterruptedException {
        if (buffer == null) {
            return 0;
        }
        int needGetSize = dataSize;
        while (isEmptyInternal()) {
            //logger.d(userName + "is empty now, wait......");
            this.wait();
            if (isDestroying) {
                logger.d(userName + ":clear is executed, exit getData");
                return 0;
            }
        }

        if (getDataSizeInternal() < dataSize) {
            needGetSize = getDataSizeInternal();
        }

        for (int i = 0; i < needGetSize; i++, head++) {
            if (head >= bufferSize) {
                head %= bufferSize;
            }
            data[i + dataOffset] = buffer[head];
        }

        this.notifyAll();

        return needGetSize;
    }

    public synchronized void clearBuffer() throws InterruptedException {
        this.notifyAll();
        tail = head = 0;
    }

    public void destroyBuffer() {
        try {
            isDestroying = true;
            clearBuffer();
        }catch (InterruptedException e) {
            e.printStackTrace();
        }

        shortBuffer = null;
        buffer = null;
    }
}
